﻿using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace Elio.Controls;
public class CustomAbsoluteLayout : AbsoluteLayout
{
    public static readonly BindableProperty ItemsProperty =
        BindableProperty.Create(
            nameof(Items),
            typeof(ObservableCollection<View>),
            typeof(CustomAbsoluteLayout),
            propertyChanged: OnObserveCollectionChanged);

    public static readonly BindableProperty PointProperty =
        BindableProperty.CreateAttached(
            nameof(Point),
            typeof(Point),
            typeof(CustomAbsoluteLayout),
            Point.Zero);
    public ObservableCollection<View> Items
    {
        get => (ObservableCollection<View>)GetValue(ItemsProperty);
        set => SetValue(ItemsProperty, value);
    }

    private static void OnObserveCollectionChanged(BindableObject bindable, object oldValue, object newValue)
    {
        CustomAbsoluteLayout layout = (CustomAbsoluteLayout)bindable;
        if (newValue is ObservableCollection<View> items)
        {
            layout.Children.Clear();
            foreach (View item in items) layout.Children.Add(item);

            items.CollectionChanged += layout.OnCollectionChanged;
        }
    }

    private async void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        static View SetBounds(View view)
        {
            Point p = CustomAbsoluteLayout.GetPoint(view);
            (double w, double h) = (view.WidthRequest, view.HeightRequest);
            AbsoluteLayout.SetLayoutBounds(view, new Rect(p.X, p.Y, w, h));
            return view;
        }

        Action action = e.Action switch
        {
            NotifyCollectionChangedAction.Add =>
                () => e.NewItems!.OfType<View>().ToList().ForEach(view => Children.Add(SetBounds(view))),
            NotifyCollectionChangedAction.Remove =>
                () => e.OldItems!.OfType<View>().ToList().ForEach(view => Children.Remove(view)),
            NotifyCollectionChangedAction.Reset => Children.Clear,

            _ => () => { }
        };

        await MainThread.InvokeOnMainThreadAsync(action.Invoke);
    }

    public static Point GetPoint(BindableObject view) => (Point)view.GetValue(PointProperty);
    public static void SetPoint(BindableObject view, Point value) => view.SetValue(PointProperty, value);
}

public static class ViewForPointExtension
{
    public static void SetPoint(this View view, int x, int y) => CustomAbsoluteLayout.SetPoint(view, new Point(x, y));
}